#pragma once
#include <iostream>
#include <cstring>
#include <string>
#include <sys/select.h>
#include "sock.hpp"
#include "log.hpp"
#include "err.hpp"

const static int gport = 8888;

#define READ_EVENT      (0X1)
#define WRITE_EVENT     (0X1<<1)
#define EXCEPT_EVENT    (0X1<<2)

typedef struct FdEvent{
    int fd;
    uint8_t event;
    string clientip;
    uint16_t clientport;
}type_t;
static const int defaultevent = 0;

class SelectServer{

    static const int N = (sizeof(fd_set) * 8);

public:
    SelectServer(uint16_t port = gport):_port(port){

    }
    void Init(){
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        for (int i = 0; i < N;i++){
            _fdarr[i].fd = defaultfd;
            _fdarr[i].event = defaultevent;
            _fdarr[i].clientport = 0;
        }
    }

    void Accepter(){
        // cout << "有一个新链接到来" << endl;

        string clientip;
        uint16_t clinetport;
        int sock = _listensock.Accept(&clientip, &clinetport);
        if(sock < 0){
            return;
        }

        //得到了对应的sock，不能在这里进行read/recv
        //因为上次select只知道有新链接，而不知道新链接的读写fd是否就绪
        //如果在这里进行读写，又会变成阻塞式
        //所以需要将sock交给select，等待读写fd就绪
        logMessage(DEBUG, "%s:%d, sock:%d", clientip.c_str(), clinetport, sock);
        //要让select进行管理，只需要把sock添加到_fdarr就行
        for (int i = 1; i < N; i++){
            if(_fdarr[i].fd==defaultfd){
                _fdarr[i].fd = sock;
                //_fdarr[i].event = READ_EVENT | WRITE_EVENT;
                _fdarr[i].event = READ_EVENT;
                _fdarr[i].clientip = clientip;
                _fdarr[i].clientport = clinetport;
                break;
            }
            //当数组满了
            if(i>=N-1){
                close(sock);
                logMessage(WARNING, "_fdarr full");
            }
        }
    }

    void Recver(int index){
        //这里的读取是有问题的，不能保证读取到一个完整的报文，需要配合协议
        char buf[1024];
        ssize_t s = recv(_fdarr[index].fd, buf, sizeof(buf) - 1, 0);   //这里的读取不会被阻塞，因为已经检测到就绪
        if(s > 0){
            buf[s-1] = 0;
            cout << _fdarr[index].clientip<< ":" << _fdarr[index].clientport << "  client# " << buf << endl;

            //回传数据也要用select管理
            string echo = buf;
            echo += "[echo]";
            send(_fdarr[index].fd, echo.c_str(), echo.size(), 0);
        }
        else if (s == 0){
            close(_fdarr[index].fd);
            logMessage(INFO, "client quit! -- fd:%d", _fdarr[index]);
            _fdarr[index].fd = defaultfd;
        }
        else{
            close(_fdarr[index].fd);
            logMessage(WARNING, "recv error, client quit! -- fd:%d", _fdarr[index]);
            _fdarr[index].fd = defaultfd;
            _fdarr[index].event = defaultevent;
            _fdarr[index].clientip.resize(0);
            _fdarr[index].clientport = 0;
        }
    }

    void HandlerEvent(fd_set &rfds, fd_set &wfds){
        for (int i = 0; i < N; i++){
            // 对select的结果进行检测
            if(_fdarr[i].fd == defaultfd){
                continue;
            }

            //判断fd类型
            //如果是读fd，且读事件已经发生
            if((_fdarr[i].event & READ_EVENT) && (FD_ISSET(_fdarr[i].fd, &rfds))){
                //处理读取  1、accept 2、recv
                if (_fdarr[i].fd == _listensock.Fd()){
                    Accepter();
                }//如果就绪的fd是在rfds中，但不是监听套接字，进行处理
                else if(_fdarr[i].fd != _listensock.Fd()){
                    Recver(i);
                }else{
                }
            }//如果是写fd，且写事件已经发生
            else if((_fdarr[i].event & WRITE_EVENT) && (FD_ISSET(_fdarr[i].fd, &wfds))){
                //处理写入

            }else{

            }
            
        }
        //接收
    }

    void Start(){
        //在网络中，新链接的到来被当做读事件就绪

        //将监听套接字添加到fd数组中
        _fdarr[0].fd = _listensock.Fd();
        _fdarr[0].event = READ_EVENT;
        while (1){
            // struct timeval timeout = {2, 0};
            // 因为rfds是输入输出型参数，会被更改，所以输入前需要对rfds进行记录
            // 因为服务器运行是sockfd的值一直在动态变化，所以maxfd也一定在变化 

            //创建fd集合
            fd_set rfds;
            fd_set wfds;
            //清空集合
            FD_ZERO(&rfds);
            FD_ZERO(&wfds);
            // //将_listensock添加到读fd集合
            // FD_SET(_listensock.Fd(), &rfds);
            //将所有fd添加到rfds
            int maxfd = _fdarr[0].fd;
            for (int i = 0; i < N; i++){
                if(_fdarr[i].fd==defaultfd){
                    continue;
                }
                //根据当前文件描述符的事件类型添加到不同的集合中
                if(_fdarr[i].event == READ_EVENT) {
                    FD_SET(_fdarr[i].fd, &rfds);
                }
                if(_fdarr[i].event == WRITE_EVENT){
                    FD_SET(_fdarr[i].fd, &wfds);
                }
                if(maxfd<_fdarr[i].fd){
                    maxfd = _fdarr[i].fd;
                }
            }

            int n = select(maxfd + 1, &rfds, &wfds, nullptr, nullptr);
            switch (n){
                case 0:
                    logMessage(DEBUG, "time out, %d:%s", errno, strerror(errno));
                    break;
                case -1:
                    logMessage(WARNING, "%d:%s", errno, strerror(errno));
                    break;
                default:
                    logMessage(DEBUG, "有一个就绪事件发生:%d", n);
                    HandlerEvent(rfds, wfds);
                    DebugPrint();
                    break;
            }
        }
    }

    void DebugPrint(){
        cout << "_fdarr[]: ";
        for (int i = 0; i < N;i++){
            if(_fdarr[i].fd!=defaultfd){
                cout << _fdarr[i].fd << " ";
            }
        }
        cout << endl;
    }

    ~SelectServer(){
        _listensock.Close();
    }

private:
    uint16_t _port;
    Sock _listensock;
    type_t _fdarr[N];
};
